package ar.edu.sccs.model.cbr;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;

import org.apache.log4j.Logger;

import ar.edu.sccs.dao.CalificationDTO;
import ar.edu.sccs.dao.CaseDTO;
import ar.edu.sccs.dao.DaoModule;
import ar.edu.sccs.dao.RepositoryDTO;
import ar.edu.sccs.entities.Case;
import ar.edu.sccs.entities.Dimension;
import ar.edu.sccs.model.cbr.similarity.SimilarityModule;
import ar.edu.sccs.monitoring.exception.SccsGenericException;
import ar.edu.sccs.monitoring.logging.SCCSLogger;
import ar.edu.sccs.util.Constants;
import ar.edu.sccs.util.SvnHelper;

public class CbrModule {

	static Logger logger = SCCSLogger.getLogger(CbrModule.class);

	private DaoModule daoModule = new DaoModule();
	private SimilarityModule similarityModule = SimilarityModule.getInstance();
	private List<CalificationDTO> califications;

	public List<Case> searchResult(Case targetCase, List<Case> semanticCases) throws SccsGenericException {
		logger.debug("CbrModule.searchResult: " + semanticCases.toString());
		List<Case> resultCases = new ArrayList<Case>();

		for (Case semanticCase : semanticCases) {
			List<Case> retrievedCases = daoModule.getCases(semanticCase);

			for (Case retrievedCase : retrievedCases) {
				// Calcula la simimlitud de cada una de las dimensiones del caso
				// recuperado
				calculateSimilarity(targetCase, retrievedCase);

				// Calcula la simimlitud del conjunto de las dimensiones del
				// caso recuperado
				calculateCaseSimilarity(targetCase, retrievedCase);

				resultCases.add(retrievedCase);
			}
		}

		// Ordena por grado de similitud los casos recuperado
		return sortResults(resultCases);
	}

	private Case calculateSimilarity(Case targetCase, Case retrievedCase) {
		similarityModule.calculateSimilarity(targetCase, retrievedCase);
		return retrievedCase;
	}

	private Case calculateCaseSimilarity(Case targetCase, Case retrievedCase) {
		Double similityValue = 0.0;
		Double weightValue = 0.0;
		int quantityValues = 0;

		for (Iterator<String> keys = targetCase.getDimensions().keySet().iterator(); keys.hasNext();) {
			String key = keys.next();
			Dimension dim = targetCase.getDimensions().get(key);
			Dimension retrieved = retrievedCase.getDimensions().get(key);
			if (retrieved != null && retrieved.getSimilarity() != null && dim.getValue() != null && dim.getWeight() != 0) {
//				logger.debug("TargetDim: " + dim.getValue());
//				logger.debug("RetrievedDim: " + retrieved.getValue() + " ~ Similarity: " + retrieved.getSimilarity());
				similityValue += retrieved.getSimilarity() * dim.getWeight();
				weightValue += dim.getWeight();
				quantityValues++;
			}
		}

//		logger.debug("similarityValue: " + similityValue + " ~ weightValue: " + weightValue);
		retrievedCase.setSimilarity(weightValue != 0 ? similityValue / weightValue : 0);
		logger.info("CbrModule.calculateCaseSimilarity: Se obtiene " + retrievedCase.getSimilarity() + " como similitud total del caso: [" + retrievedCase.getCaseId() + "]");
		return retrievedCase;
	}

	private List<Case> sortResults(List<Case> resultCases) {
		Collections.sort(resultCases, new SimilarityComparator());
		if (resultCases.size() < Constants.Cbr.LIMIT_RESULT) {
			logger.info("CbrModule.sortResults: Se retornan " + resultCases.size() + " resultados");
			return resultCases;
		} else {
			logger.info("CbrModule.sortResults: Se retornan " + resultCases.size() + " resultados");
			return resultCases.subList(0, Constants.Cbr.LIMIT_RESULT);
		}
	}

	public class SimilarityComparator implements Comparator<Case> {
		public int compare(Case o1, Case o2) {
			Double double1 = o1.getSimilarity();
			Double double2 = o2.getSimilarity();
			if(!double1.equals(double2))
				return double2.compareTo(double1);
			else{
				Double calif1 = getCalificationValue(o1.getCalificationId());
				Double calif2 = getCalificationValue(o2.getCalificationId());
				return calif2.compareTo(calif1);
			}
		}
	}

	@Deprecated
	public String recoverCode(Case selectedCase) {
		try {
			RepositoryDTO repository = daoModule.findRepository(selectedCase.getProjectId());
			return SvnHelper.recoverCode(selectedCase, repository);
		} catch (Exception e) {
			logger.error("CbrModule.recoverCode ERROR: ", e);
			return null;
		}
	}

	public String recoverCode(Integer selectedCaseId) {
		try {
			CaseDTO selectedCase = daoModule.findCase(selectedCaseId);
			RepositoryDTO repository = daoModule.findRepository(selectedCase.getProjectId());
			return SvnHelper.recoverCode(selectedCase, repository);
		} catch (Exception e) {
			logger.error("CbrModule.recoverCode ERROR: ", e);
			return null;
		}
	}
	
	private Double getCalificationValue(Integer id){
		if (califications == null)
			try {
				califications = daoModule.loadCalification();
			} catch (SccsGenericException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
				return null;
			}
		String value = null;
		for (Iterator<CalificationDTO> iterator = califications.iterator(); iterator.hasNext();) {
			CalificationDTO calif = iterator.next();
			if(calif.getCalification_id().equals(id)){
				value = calif.getCalification_value();
				break;
			}
		}
		return Double.valueOf(value);
	}
}
